//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//

#include <exception>
#include <stdexcept>
#include <string>
#include <sstream>

#include <cmath>
#include <cstring>
#include <cerrno>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "cpp-lib/posix/wrappers.h"
#include "cpp-lib/realtime.h"
  
using namespace cpl::detail_ ;


namespace {

   ::clockid_t const MY_CLOCK = CLOCK_REALTIME  ; // Std. Posix
// ::clockid_t const MY_CLOCK = CLOCK_MONOTONIC ; // QNX
// ::clockid_t const MY_CLOCK = CLOCK_HIGHRES   ; // Solaris
// ::clockid_t const MY_CLOCK = CLOCK_SGI_CYCLE ;

// int const MY_SIGNAL = SIGUSR1 ;
int const MY_SIGNAL = SIGRTMIN ;


// Wait for signal s, discarding any information.

inline void wait4( ::sigset_t const& ss ) {
	
  siginfo_t dummy ;
  STRERROR_CHECK( ::sigwaitinfo( &ss , &dummy ) ) ;

}


} // anonymous namespace


cpl::util::realtime_scheduler::realtime_scheduler( double const& dt ) {

  always_assert( dt > 0 ) ;

  // Block MY_SIGNAL.  Will be handled by sigwaitinfo().

  sigs = block_signal( MY_SIGNAL ) ;

  // Set up timer
 
  ::sigevent my_event ;
  my_event.sigev_notify = SIGEV_SIGNAL ;
  my_event.sigev_signo  = MY_SIGNAL    ;
  // (union: alternative is sival_ptr)
  my_event.sigev_value.sival_int = 0            ;   

  STRERROR_CHECK
  ( ::timer_create( MY_CLOCK , &my_event , &timer ) ) ;
  
  ::itimerspec its ;
  its.it_value    = to_timespec( 1e-9 ) ; // arm: need nonzero value
  its.it_interval = to_timespec( dt   ) ;

  STRERROR_CHECK( ::timer_settime( timer , 0 , &its , 0 ) ) ;
  
} ;


double cpl::util::realtime_scheduler::time() {

  ::timespec t ;
  STRERROR_CHECK( ::clock_gettime( MY_CLOCK , &t ) ) ;

  return to_double( t ) ;

}
    

double cpl::util::realtime_scheduler::wait_next() {

  wait4( sigs ) ;
  return time() ;

}


cpl::util::realtime_scheduler::~realtime_scheduler()
{ ::timer_delete( timer ) ; }


// 
// Some RT stuff snippets, may be incorporated in the future...
//

#if 0
void check_rtsig() {

  errno = 0 ;
  long const i = ::sysconf( _SC_REALTIME_SIGNALS ) ;
  if( errno ) { die( "sysconf" ) ; }

  if( i <= 0 ) {
    std::cerr << "realtime signals not supported\n" ;
    std::exit( 1 ) ;
  }
  
}



void set_sched() {

  sched_param sp ;

  sp.sched_priority = sched_get_priority_max( SCHED_RR ) ;

  if( sched_setscheduler( 0 , SCHED_RR , &sp ) < 0 ) {
    
    std::cerr << "warning: couldn't set scheduling parameters\n" ;

  }

}


    timespec res ;
    if( ::clock_getres( MY_CLOCK , &res ) < 0 )
    { die( "clock_getres" ) ; }


#endif 
